package com.mathpractise.repository.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.mathpractise.model.StatisticsReport;
import com.mathpractise.model.TrainingSession;
import com.mathpractise.repository.StatisticsRepository;
import com.mathpractise.repository.TrainingRepository;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 基于文件的数据访问实现类
 */
@Repository
public class FileBasedRepositoryImpl implements TrainingRepository, StatisticsRepository {
    
    private static final String TRAINING_DIR = "data/training/sessions";
    private static final String STATISTICS_DIR = "data/statistics/reports";
    
    private ObjectMapper objectMapper;
    private Cache<String, TrainingSession> trainingSessionCache;
    private Cache<String, StatisticsReport> statisticsReportCache;
    
    @PostConstruct
    public void init() {
        // 初始化目录
        createDirectories();
        
        // 初始化 ObjectMapper
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        
        // 初始化缓存
        trainingSessionCache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
        
        statisticsReportCache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
    }
    
    private void createDirectories() {
        try {
            Files.createDirectories(Paths.get(TRAINING_DIR));
            Files.createDirectories(Paths.get(STATISTICS_DIR));
        } catch (IOException e) {
            throw new RuntimeException("Failed to create data directories", e);
        }
    }
    
    @Override
    public void save(TrainingSession session) {
        try {
            String fileName = TRAINING_DIR + File.separator + session.getSessionId() + ".json";
            objectMapper.writeValue(new File(fileName), session);
            trainingSessionCache.put(session.getSessionId(), session);
        } catch (IOException e) {
            throw new RuntimeException("Failed to save training session: " + session.getSessionId(), e);
        }
    }
    
    @Override
    public Optional<TrainingSession> findById(String sessionId) {
        // 先从缓存中查找
        TrainingSession session = trainingSessionCache.getIfPresent(sessionId);
        if (session != null) {
            return Optional.of(session);
        }
        
        // 从文件中读取
        try {
            String fileName = TRAINING_DIR + File.separator + sessionId + ".json";
            File file = new File(fileName);
            if (!file.exists()) {
                return Optional.empty();
            }
            
            session = objectMapper.readValue(file, TrainingSession.class);
            trainingSessionCache.put(sessionId, session);
            return Optional.of(session);
        } catch (IOException e) {
            throw new RuntimeException("Failed to read training session: " + sessionId, e);
        }
    }
    
    @Override
    public void update(TrainingSession session) {
        save(session);
    }
    
    @Override
    public void delete(String sessionId) {
        try {
            String fileName = TRAINING_DIR + File.separator + sessionId + ".json";
            Files.deleteIfExists(Paths.get(fileName));
            trainingSessionCache.invalidate(sessionId);
        } catch (IOException e) {
            throw new RuntimeException("Failed to delete training session: " + sessionId, e);
        }
    }
    
    @Override
    public List<TrainingSession> findAll() {
        List<TrainingSession> sessions = new ArrayList<>();
        File dir = new File(TRAINING_DIR);
        File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
        
        if (files != null) {
            for (File file : files) {
                try {
                    TrainingSession session = objectMapper.readValue(file, TrainingSession.class);
                    sessions.add(session);
                } catch (IOException e) {
                    // Log error but continue processing other files
                    System.err.println("Failed to read training session file: " + file.getName());
                }
            }
        }
        
        return sessions;
    }
    
    @Override
    public void save(StatisticsReport report) {
        try {
            String fileName = STATISTICS_DIR + File.separator + report.getSessionId() + ".json";
            objectMapper.writeValue(new File(fileName), report);
            statisticsReportCache.put(report.getSessionId(), report);
        } catch (IOException e) {
            throw new RuntimeException("Failed to save statistics report: " + report.getSessionId(), e);
        }
    }
    
    @Override
    public Optional<StatisticsReport> findBySessionId(String sessionId) {
        // 先从缓存中查找
        StatisticsReport report = statisticsReportCache.getIfPresent(sessionId);
        if (report != null) {
            return Optional.of(report);
        }
        
        // 从文件中读取
        try {
            String fileName = STATISTICS_DIR + File.separator + sessionId + ".json";
            File file = new File(fileName);
            if (!file.exists()) {
                return Optional.empty();
            }
            
            report = objectMapper.readValue(file, StatisticsReport.class);
            statisticsReportCache.put(sessionId, report);
            return Optional.of(report);
        } catch (IOException e) {
            throw new RuntimeException("Failed to read statistics report: " + sessionId, e);
        }
    }
    
    @Override
    public void deleteBeforeDate(Date beforeDate) {
        try {
            File dir = new File(STATISTICS_DIR);
            File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
            
            if (files != null) {
                for (File file : files) {
                    BasicFileAttributes attrs = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
                    Date fileDate = new Date(attrs.creationTime().toMillis());
                    
                    if (fileDate.before(beforeDate)) {
                        Files.deleteIfExists(file.toPath());
                        // Also invalidate cache entry if present
                        String sessionId = file.getName().replace(".json", "");
                        statisticsReportCache.invalidate(sessionId);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("Failed to delete statistics reports before date: " + beforeDate, e);
        }
    }
}